﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Threading;
using Mono.Terminal;

namespace SerialCommander {

    class Program {

        static string _BannerSeparator = "******************************************"; 
        static string _Banner = string.Format("** Serial Commander Version {0}", AssemblyVersion);
        static string _Title = "Serial Commander";
        static List<string> _AutoCompleteList = new List<string>();
        static Dictionary<string, object> _Variables = new Dictionary<string, object>();
        static LineEditor le = null;

        static void Main(string[] args) {

            Console.Title = _Title;
            le = new LineEditor("Serial");

            SerialPort port = new SerialPort();

            bool connected = false;
            string commPort = null;

            while (!connected) {
                
                try {
                    Console.Write("Port: ");
                    commPort = Console.ReadLine().Trim().ToUpper();

                    int portNum;
                    if (int.TryParse(commPort, out portNum))
                        commPort = string.Format("COM{0}", portNum); 

                    port = new SerialPort(commPort, 9600, Parity.None, 8, StopBits.One);
                    port.Open();
                    connected = true;
                    Console.WriteLine(_BannerSeparator);
                    Console.WriteLine(_Banner);
                    Console.WriteLine("** {0} opened", commPort);
                    Console.Title = string.Format("{0} Connected to {1} port", _Title, commPort);
                } catch (Exception) {
                    Console.WriteLine("{0}: unable to connect!", commPort);
                }
            }

            Console.WriteLine("** Starting background port thread...");

            Thread th = new Thread(new ParameterizedThreadStart(Read));
            th.IsBackground = true;
            th.Start(port);

            Console.Write("*");

            System.Threading.Thread.Sleep(100);
            Console.WriteLine(" Acepting data...");
            Console.WriteLine(_BannerSeparator);
            Console.WriteLine();

            SetVariable("prompt", commPort);
            SetVariable("timestamp", 1);
            SetVariable("echo", 1);
            
            le.AutoCompleteEvent += new LineEditor.AutoCompleteHandler(AutoCompleteCommand);
            le.TabAtStartCompletes = true;
            string s;

            while ((s = le.Edit(string.Format("{0}: ", GetVariable("prompt")), string.Empty)) != null) {

                string s2 = s.Trim();
                string[] tokens;

                if (s2.Length > 0 && s2[0] == '/') {
                    s2 = s2.Substring(1);

                    tokens = s2.Split(' ', '\t');
                    if (tokens.Length > 0) {
                        string command = tokens[0];

                    }

                } else {

                    foreach (string key in _Variables.Keys) {
                        string varStr = string.Format("${0}", key);
                        if (s.Contains(varStr))
                            s.Replace(varStr, _Variables[key].ToString());
                    }

                    if (GetVariable("echo").Equals(1))
                        Console.WriteLine(">>> {0}", s);

                    port.Write(s);
                }

                tokens = s2.Split(' ', '\t');
                for (int i = 0; i < tokens.Length; i++) {
                    if (tokens[i] != string.Empty && !_AutoCompleteList.Contains(tokens[i]))
                        _AutoCompleteList.Add(tokens[i]);
                }
            }

        }

        public static void PrintVariables(string variable) {
            if (variable == null) {
                foreach (string key in _Variables.Keys) {
                    Console.WriteLine(" {0}\t\t{1}", key, _Variables[key]);
                }
            } else {
                if (_Variables.ContainsKey(variable))
                    Console.WriteLine(" {0}\t\t{1}", variable, _Variables[variable]);
                else
                    Console.WriteLine("'{0}' not found", variable);
            }
        }

        public static object GetVariable(string variable) {

            variable = variable.ToLower();

            if (_Variables.ContainsKey(variable))
                return _Variables[variable];
            else
                return string.Empty;
        }

        public static void SetVariable(string variable, object value) {
            SetVariable(variable, value, false);
        }

        public static void SetVariable(string variable, object value, bool print) {

            variable = variable.ToLower();

            if (_Variables.ContainsKey(variable)) {

                Type vType = _Variables[variable].GetType();

                try {    
                    object vObj = Convert.ChangeType(value, vType);
                    _Variables[variable] = vObj;
                } catch {
                    Console.WriteLine("Could not convert '{0}' to {1}", value, vType);
                    return;
                }
            } else {
                _Variables.Add(variable, value);
            }

            if (print)
                Console.WriteLine("-> {0} = {1}", variable, value);

        }

        public static LineEditor.Completion AutoCompleteCommand(string text, int position) {

            string token = null;

            for (int i = position - 1; i >= 0; i--) {
                if (Char.IsWhiteSpace(text[i])) {
                    token = text.Substring(i + 1, position - i - 1);
                    break;
                } else if (i == 0)
                    token = text.Substring(0, position);
            }

            List<string> results = new List<string>();

            if (token == null) {
                token = string.Empty;
                results.AddRange(_AutoCompleteList);
            } else {
                for (int i = 0; i < _AutoCompleteList.Count; i++) {
                    if (_AutoCompleteList[i].StartsWith(token)) {
                        string result = _AutoCompleteList[i];
                        results.Add(result.Substring(token.Length, result.Length - token.Length));
                    }
                }
            }

            return new LineEditor.Completion(token, results.ToArray());
        }

        public static void Read(object obj) {
            SerialPort port = obj as SerialPort;
            if (port != null) {

                Console.Write("*");

                while (true) {

                    DateTime start = DateTime.Now;

                    string line = port.ReadLine();

                    Console.Write('\r');
                    for (int i = 0; i < Console.WindowWidth - 1; i++)
                        Console.Write(' ');

                    Console.Write('\r');
                    
                    if (_Variables["timestamp"].Equals(1))
                        Console.WriteLine("[{0}] {1}", DateTime.Now.ToString("hh:mm:ss"), line);
                    else if (_Variables["timestamp"].Equals(2))
                        Console.WriteLine("[{0}] {1}", start.Subtract(DateTime.Now), line);
                    else if (_Variables["echo"].Equals(1))
                        Console.WriteLine("<<< {1}", line);
                    else
                        Console.WriteLine("line");

                    le.Render(false);
                }
            }
        }

        public static string AssemblyVersion {
            get {
                return Assembly.GetExecutingAssembly().GetName().Version.ToString();
            }
        }



    }



}
